﻿using Ma.Bussiness.AttributeExtend;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Ma.Bussiness.IOCExtend
{
    /// <summary>
    /// 控制反转接口
    /// </summary>
    public interface IContainer
    {
        /// <summary>
        /// 依赖注入
        /// </summary>
        /// <typeparam name="TFrom"></typeparam>
        /// <typeparam name="TTo"></typeparam>
        void RegisterType<TFrom, TTo>();
        /// <summary>
        /// 创建对象
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        T Resolve<T>();

    }

    /// <summary>
    /// 容器 -工厂
    /// </summary>
    public class Container : IContainer
    {
        /// <summary>
        /// 字典保存依赖注入的对象集
        /// </summary>
        private Dictionary<string, Type> _typeCache = new Dictionary<string, Type>();

        public void RegisterType<TFrom, TTo>() => _typeCache.Add(key: typeof(TFrom).FullName, typeof(TTo));

        public T Resolve<T>()
        {
            Type type = this._typeCache[typeof(T).FullName];
            return (T)this.CreateObject(type);
        }

        /// <summary>
        /// 递归去实现构造函数创建实体
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        private object CreateObject(Type type)
        {
            System.Reflection.ConstructorInfo[] ctors = type.GetConstructors();
            ConstructorInfo info = null;
            if (ctors.Any(a => a.IsDefined(typeof(ElevenInjectionConstructorAttribute), true)))
            {
                info = ctors.FirstOrDefault(a => a.IsDefined(typeof(ElevenInjectionConstructorAttribute), true));
            }
            else
            {
                info = ctors.OrderByDescending(a => a.GetParameters().Length).FirstOrDefault();
            }
            List<object> args = new List<object>();
            foreach(var cur in info.GetParameters())
            {
                Type type1 = cur.ParameterType;
                Type type2 = this._typeCache[type1.FullName];
                if (type2 != null)
                {
                    var pare= this.CreateObject(type2);
                    args.Add(pare);
                }
            }
            return Activator.CreateInstance(type, args.ToArray());
        }
    }
}
